// — — — — — — — — — — — — glwidget.cpp — — — — — — — —
#include "GLWidget.h"
GLWidget::GLWidget(QWidget *parent) : QGLWidget(parent)
{
}




QImage loadTexture2(char *filename, GLuint &textureID){
    glEnable(GL_TEXTURE_2D); // Enable texturing

    glGenTextures(1, &textureID); // Obtain an id for the texture
    glBindTexture(GL_TEXTURE_2D, textureID); // Set as the current texture

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

    QImage im(filename);
    QImage tex = QGLWidget::convertToGLFormat(im);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.width(), tex.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex.bits());

    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

    glDisable(GL_TEXTURE_2D);

    return tex;
}




void  GLWidget::init(){
   tex_ =  loadTexture2((char*)"d://temp//people.png", backgroundimage_);
   init_corner_anchors(corners_, anchors_ , tex_ );
   width_ = tex_.width();
   height_ = tex_.height();
}


void GLWidget::initializeGL()
{
    init();
  glClearColor(0, 0, 1, 1); //define blue color as background color
}


void GLWidget::resizeGL(int w, int h)
{
}


float bezierPoint(float x0, float x1, float x2, float x3, float t) {
    float   ax, bx, cx;
    float   ay, by, cy;
    float   t2, t3;
    float   x;

    // polynomial coefficients
    cx = 3.0f * (x1 - x0);
    bx = 3.0f * (x2 - x1) - cx;
    ax = x3 - x0 - cx - bx;

    t2 = t * t;
    t3 = t2 * t;
    x = (ax * t3) + (bx * t2) + (cx * t) + x0;

    return x;
}


float ofRandom(float x, float y) {
    float high = std::max(x, y);
    float low = std::min(x, y);
    return std::max(low, (low + ((high - low) * rand() / float(RAND_MAX))) * (1.0f - std::numeric_limits<float>::epsilon()));
}

void  GLWidget::init_corner_anchors(QVector<QPoint> &corners , QVector<QPoint>& anchors, QImage &img){


    int width = img.width();
    int height = img.height();

    int startx = 0;
    int starty = 0;
    corners.push_back(QPoint( 0,   +  0));
    corners.push_back(QPoint( startx + width, starty +  0));
    //corners.push_back(QPoint( 0, 0));
    corners.push_back(QPoint(startx + width,  starty +  height));
    corners.push_back(QPoint( startx + 0,  starty +  height));

    //anchors.push_back(QPoint(-   width, -height * 0.33333));
    //anchors.push_back(QPoint(startx + 0,  starty +qRound(height * 0.33333  ) ));
    anchors.push_back(QPoint(width / 2 ,  height / 2));
    anchors.push_back(QPoint(startx + qRound(width * 0.33333 ), starty + 0));
    anchors.push_back(QPoint(startx + qRound(width * 0.66666), starty + 0));
    anchors.push_back(QPoint(startx + width,  starty + qRound(height * 0.3333)));
    anchors.push_back(QPoint(startx + width, starty + qRound(height * 0.66666)));
    anchors.push_back(QPoint(startx + qRound(width * 0.66666), starty + height));
    anchors.push_back(QPoint(startx + qRound(width * 0.33333 ), starty + height));
    anchors.push_back(QPoint(startx + 0, starty + qRound(height * 0.66666)));


//    corners.resize(4);
//    float rnd_x = ofRandom(0.1, 0.6);
//    float rnd_y = ofRandom(0.1, 0.6);
//    corners[0] = QPoint(width * (0.0 + rnd_x), height * (0.0 + rnd_y));
//    corners[1] = QPoint(width * (0.3 + rnd_x), height * (0.0 + rnd_y));
//    corners[2] = QPoint(width * (0.3 + rnd_x), height * (0.3 + rnd_y));
//    corners[3] = QPoint(width * (0.0 + rnd_x), height * (0.3 + rnd_y));

//    // top left first then clockwise
//    anchors.resize(8);
//    anchors[0] = QPoint(width * (0.0 + rnd_x), height * (0.1 + rnd_y));
//    anchors[1] = QPoint(width * (0.1 + rnd_x), height * (0.0 + rnd_y));
//    anchors[2] = QPoint(width * (0.2 + rnd_x), height * (0.0 + rnd_y));
//    anchors[3] = QPoint(width * (0.3 + rnd_x), height * (0.1 + rnd_y));
//    anchors[4] = QPoint(width * (0.3 + rnd_x), height * (0.2 + rnd_y));
//    anchors[5] = QPoint(width * (0.2 + rnd_x), height * (0.3 + rnd_y));
//    anchors[6] = QPoint(width * (0.1 + rnd_x), height * (0.3 + rnd_y));
//    anchors[7] = QPoint(width * (0.0 + rnd_x), height * (0.2 + rnd_y));





    B_[0][0] ={0 ,          0 , 0};
    B_[1][0] ={width / 3    ,0 , 0};
    B_[2][0] ={width * 2 / 3 ,    0 , 0};
    B_[3][0] ={width  ,0 , 0};

    B_[0][1] ={0 ,                  height / 3 , 0};
    B_[1][1] ={width   ,         height / 3 , 0};
    B_[2][1] ={width  * 2/  3 ,     height / 3 , 0};
    B_[3][1] ={width ,              height / 3 , 0};


    B_[0][2] ={0 ,                  height * 2 / 3 , 0};
    B_[1][2] ={width / 3 ,          height * 2 / 3 , 0};
    B_[2][2] ={width * 2 / 3 ,      height * 2 / 3 , 0};
    B_[3][2] ={width  ,             height * 2 / 3 , 0};

    B_[0][3] ={0 ,                  height , 0};
    B_[1][3] ={width / 3 ,          height , 0};
    B_[2][3] ={width * 2 / 3 ,      height , 0};
    B_[3][3] ={width  ,             height , 0};



}



#include <chrono>
#include <string>
#include <unordered_map>
#include <fstream>
using namespace std;
#if 1
struct  TimeTrace {

    unordered_map<string, double> code_block_;
    unordered_map<string, _CHRONO time_point<_CHRONO system_clock>> code_block_point_;
    void begin_tick(string curr_block) {

        tick1_ = std::chrono::system_clock::now();;

        code_block_point_[curr_block] = tick1_;
    }
    void end_tick(string curr_block) {

        auto tick2 = std::chrono::system_clock::now();
        std::chrono::duration<double> elapsed_seconds = tick2 - code_block_point_[curr_block];  // need to do  ,
        //int msecs = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_seconds).count();// convert to ms ;
        int msecs = std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed_seconds).count();// convert to ms ;
        msecs = msecs / 1000000;
        code_block_[curr_block] = code_block_[curr_block] + msecs;
        cnt++;
    }

    void dump_time() {
        for (auto ele : code_block_) {
            int avg = code_block_[ele.first] /  cnt;
            string msg = ele.first +"---" + std::to_string(avg) + "\n";
            write(msg);

        }
    }

    void write(std::string str) {
        std::ofstream myfile;
        myfile.open("d://temp//bright.txt", std::ios_base::app);
        // myfile.open("/Users/lmj/plugin.txt");
        //  Check if the file was opened successfully
        if (myfile.is_open()) {
            // Write a line to the file
            myfile << str;
            // Close the file
            myfile.close();
        }
    }
    bool begin_ = false;
    bool end_ = true;
    _CHRONO time_point<_CHRONO system_clock> tick1_;
    int cnt = 0;
    // int tick2_ = 0;
};

#endif


static TimeTrace g_time;

#include <QDebug>







Vertex3D GLWidget::bezier(double u, double v)
{
    double u2 = u * u;
    double u3 = u2 * u;
    double v2 = v * v;
    double v3 = v2 * v;

    double U[4] = {1, u, u2, u3};
    double V[4] = {1, v, v2, v3};

    static const double M[4][4] = {
                       { 1, 0, 0, 0},
                       {-3, 3, 0, 0},
                       { 3,-6, 3, 0},
                       {-1, 3,-3, 1},
                       };
    //UM
    double UM[4] = {0};
    for (int i=0; i<4; i++)
       for (int j=0; j<4; j++)
           UM[i] += U[j]*M[j][i];

    //UMB
    double UMBx[4] = {0};
    double UMBy[4] = {0};
    double UMBz[4] = {0};
    for (int i=0; i<4; i++)
       for (int j=0; j<4; j++)
       {
           UMBx[i] += UM[j] * B_[j][i].x;
           UMBy[i] += UM[j] * B_[j][i].y;
           UMBz[i] += UM[j] * B_[j][i].z;
       }

    //UMB(M^T)
    double UMBMtx[4] = {0};
    double UMBMty[4] = {0};
    double UMBMtz[4] = {0};
    for (int i=0; i<4; i++)
       for (int j=0; j<4; j++)
       {
           UMBMtx[i] += UMBx[j] * M[i][j];
           UMBMty[i] += UMBy[j] * M[i][j];
           UMBMtz[i] += UMBz[j] * M[i][j];
       }

    //UMB(M^T)V
   Vertex3D res = {0,0,0};
    for (int i=0; i<4; i++)
    {
       res.x += UMBMtx[i] * V[i];
       res.y += UMBMty[i] * V[i];
       res.z += UMBMtz[i] * V[i];
    }

    return res;
}









void GLWidget::paintGL()
{

    g_time.begin_tick("paint");

setGeometry(0, 0, width_, height_);
glViewport(-0, -0, width_ , height_ );
    int gridRes = 20;
    QVector<QVector<QPoint>> bezSurfPoints(gridRes + 1 ,QVector<QPoint>(gridRes + 1,QPoint(0,0)));


#if 0
    for(int i = 0; i <= gridRes; i++) {
        for(int j = 0; j <= gridRes; j++) {
            float start_x = bezierPoint(corners_[0].x(), anchors_[0].x(), anchors_[7].x(), corners_[3].x(), (float)j/gridRes);
            float end_x = bezierPoint(corners_[1].x(), anchors_[3].x(), anchors_[4].x(), corners_[2].x(),   (float)j/gridRes);
            float start_y = bezierPoint(corners_[0].y(), anchors_[0].y(), anchors_[7].y(), corners_[3].y(), (float)j/gridRes);
            float end_y = bezierPoint(corners_[1].y(), anchors_[3].y(), anchors_[4].y(), corners_[2].y(),   (float)j/gridRes);

            float x = bezierPoint(start_x, ((anchors_[1].x() - anchors_[6].x()) * (1.0f - (float)j/gridRes)) + anchors_[6].x(), ((anchors_[2].x() - anchors_[5].x()) * (1.0f - (float)j/gridRes)) + anchors_[5].x(), end_x, (float)i/gridRes);
            float y = bezierPoint(start_y, ((anchors_[1].y() - anchors_[6].y()) * (1.0f - (float)j/gridRes)) + anchors_[6].y(), ((anchors_[2].y() - anchors_[5].y()) * (1.0f - (float)j/gridRes)) + anchors_[5].y(), end_y, (float)i/gridRes);

        // 左移 width/2 下移 height /2
            x = x * 2  - width_ ;
            y = y * 2 -  height_ ;
        bezSurfPoints[i][j] = QPoint(x, y);
        //bezSurfPoints[i][j] = QPoint(i, j);
        }
    }

#endif

    for(int i = 0; i <= gridRes; i++) {
        for(int j = 0; j <= gridRes; j++) {

           auto rlt =  bezier(i / float(gridRes)  , j /  float(gridRes));
           float x = rlt.x;
           float y = rlt.y;
        // 左移 width/2 下移 height /2
            x = x * 2  - width_ ;
            y = y * 2 -  height_ ;
        bezSurfPoints[i][j] = QPoint(x, y);
        //bezSurfPoints[i][j] = QPoint(i, j);
        }
    }



#if 1

    int width = width_;
    int height = height_;



        int scale = 1;
        for(int i = 0; i < gridRes; i++) {
            for(int j = 0; j < gridRes; j++) {
                glEnable(GL_TEXTURE_2D);
                 glBindTexture(GL_TEXTURE_2D, 1);
                glBegin(GL_QUADS);
                qDebug()<< bezSurfPoints[i][j].x() << "," << bezSurfPoints[i][j].y();

                     glTexCoord2f((i) * (tex_.width()/gridRes / float(width)), (j) * (tex_.height()/gridRes) / float(height));
                    glVertex2f( scale* bezSurfPoints[i][j].x()  / float(width)   ,  scale* bezSurfPoints[i][j].y() / float(height));

                    glTexCoord2f((i+1) * (tex_.width()/gridRes / float(width)), (j) * (tex_.height()/gridRes) / float(height));
                    glVertex2f( scale * bezSurfPoints[i+1][j].x()  / float(width),  scale * bezSurfPoints[i+1][j].y() / float(height));

                    glTexCoord2f((i+1) * (tex_.width()/gridRes) / float(width), (j+1) * (tex_.height()/gridRes) / float(height));
                    glVertex2f( scale *bezSurfPoints[i+1][j+1].x()  / float(width),  scale *bezSurfPoints[i+1][j+1].y() / float(height));

                    glTexCoord2f((i) * (tex_.width()/gridRes) / float(width), (j+1) * (tex_.height()/gridRes) / float(height));
                    glVertex2f( scale * bezSurfPoints[i][j+1].x()  / float(width),  scale * bezSurfPoints[i][j+1].y() / float(height));

                glEnd();
              //  glDisable(GL_TEXTURE_2D);
            }
        }


        GLubyte* pPixelData;
        pPixelData = (GLubyte*)malloc(width_ * height_ * 4 * sizeof(GLubyte));//分配内存
        if (pPixelData == 0)
            return ;
        glReadBuffer(GL_FRONT);//保存窗口渲染的结果
        glPixelStorei(GL_UNPACK_ALIGNMENT, 4);//解压窗口数据结构
        glReadPixels(0, 0, width_, height_, GL_RGBA, GL_UNSIGNED_BYTE, pPixelData);//存储像素数据

        QImage img((uchar*)pPixelData, width_, height_, QImage::Format_ARGB32);
        QImage rlt(img.size(), QImage::Format_ARGB32);
        for(int y =0; y < img.height();y++){
            uchar * offset = img.scanLine(y);
            uchar * dst = rlt.scanLine(rlt.height() - y - 1);
            for (int x = 0; x < img.width(); ++x, offset = offset +4, dst = dst + 4) {
                dst[2]  = offset[0];
                dst[1] = offset[1];
                dst[0] = offset[2];
                dst[3] = offset[3];
            }


        }

    g_time.end_tick("paint");
    g_time.dump_time();
    rlt.save("d://temp//time.png");




//    glClear(GL_COLOR_BUFFER_BIT);
//    glColor3f(1,0,0);
//    glBegin(GL_LINES);
//    glVertex2f(0 ,0);
//    glVertex2f(-0.99, 0.99);
//   // glVertex2f(-0.3 , -0.3);


#endif





    int d = 1;

}

void GLWidget::mousePressEvent(QMouseEvent *event)
{


            GLubyte* pPixelData;
            pPixelData = (GLubyte*)malloc(width_ * height_ * 4 * sizeof(GLubyte));//分配内存
            if (pPixelData == 0)
                return ;
            glReadBuffer(GL_FRONT);//保存窗口渲染的结果
            glPixelStorei(GL_UNPACK_ALIGNMENT, 4);//解压窗口数据结构
            glReadPixels(0, 0, width_, height_, GL_RGBA, GL_UNSIGNED_BYTE, pPixelData);//存储像素数据

            QImage img((uchar*)pPixelData, width_, height_, QImage::Format_ARGB32);
            QImage rlt(img.size(), QImage::Format_ARGB32);
            for(int y =0; y < img.height();y++){
                uchar * offset = img.scanLine(y);
                uchar * dst = rlt.scanLine(rlt.height() - y - 1);
                for (int x = 0; x < img.width(); ++x, offset = offset +4, dst = dst + 4) {
                    dst[2]  = offset[0];
                    dst[1] = offset[1];
                    dst[0] = offset[2];
                    dst[3] = offset[3];
                }


            }
            rlt.save("d://temp//rltxxx.png");



}
